﻿#include "dxstdafx.h"
#include "CStrategyProvinceBorder.h"
#include "../CStrategyDefine.h"
#include "CStrategyRenderCommon.h"
#include "Data/DataMgr.h"
#include "Data/ProgramData/ProgramDataMgr.h"
#include "Data/ProgramData/ProvinceData.h"

static const long long RGBMAX = _stRGB(255, 255, 255).Trans2DWORD() + 1;
static const WORD c_nPlusHalfMaxWidth = c_nTerrainWidth + 1;
static const WORD c_nPlusHalfMaxHeight = c_nTerrainHeight + 1;

extern CStrategyRenderData g_StrategyRenderData;

CStrategyProvinceBorder::CStrategyProvinceBorder()
{
	m_pDecl = NULL;
	m_pEffect = NULL;
}

CStrategyProvinceBorder::~CStrategyProvinceBorder()
{
	for (auto& it : m_listBorderVerticInfo)
	{
		SAFE_RELEASE(it.pVB);
		SAFE_RELEASE(it.pIB);
	}
	SAFE_RELEASE(m_pDecl);
	SAFE_RELEASE(m_pEffect);
}

HRESULT CStrategyProvinceBorder::OnCreateDevice(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	
	D3DVERTEXELEMENT9 decl[] =
	{
		{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
		{ 0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
		D3DDECL_END()
	};
	V_RETURN(pd3dDevice->CreateVertexDeclaration(decl, &m_pDecl));

	LPD3DXBUFFER pCode = NULL;
	std::wstring szRcPathOut;
	CommonFunc::RelativePath2FullPath(L"\\ResFile\\fx\\Strategy\\StrategyBorderRender.fx", szRcPathOut);
	if (FAILED( hr = D3DXCreateEffectFromFile(pd3dDevice, szRcPathOut.c_str(), NULL, NULL,
		0, NULL, &m_pEffect, &pCode)))
	{
		LPVOID pBufErr = pCode->GetBufferPointer();
		char* ErrInfo = (char*)pBufErr;
		pCode->Release();
		return hr;
	}
	CommonFunc::CheckFxWarning(pCode);

	return hr;
}

HRESULT CStrategyProvinceBorder::OnResetDevice(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	V_RETURN(CreateMeshWhenResetDevice(pd3dDevice));
	V_RETURN(m_pEffect->SetTechnique("Border"));
	V_RETURN(m_pEffect->SetTexture("BorderDiffuse", g_StrategyRenderData.m_pTexBorderDiffuse));
	V_RETURN(m_pEffect->SetVector("g_vSelectionColor", &D3DXVECTOR4(0.97f, 1.f, 0.31f, 1.f)));
	V_RETURN(m_pEffect->SetFloat("g_fSeaLevelHeight", c_nSeaLevelHeight * c_fHeightMapScale));
	return hr;
}

void CStrategyProvinceBorder::OnFrameMove(const D3DXMATRIXA16* pWorldViewProject, const D3DXVECTOR4* vCamLookAtDir)
{
	HRESULT hr = S_OK;
	V(m_pEffect->SetMatrix("g_matWorldViewProject", pWorldViewProject));
	V(m_pEffect->SetVector("g_vCamLookAtDir", vCamLookAtDir));
}

void CStrategyProvinceBorder::OnFrameRender(IDirect3DDevice9* pd3dDevice, float fTime)
{
	HRESULT hr = S_OK;
	V(pd3dDevice->SetVertexDeclaration(m_pDecl));

	std::set<int> set_RenderSquare;
	if (!g_StrategyRenderData.GetRenderSquareSet(&set_RenderSquare))
	{
		return;
	}
	for (const auto& itInfo : m_listBorderVerticInfo)
	{
		bool bRender = CommonFunc::IsTwoSetHaveSameElement<int>(set_RenderSquare, itInfo.setAreaIn);
		if (bRender)
		{
			bool bFlicker = false;	// 边界是否闪烁
			if (itInfo.pAdjacentProvinceA && itInfo.pAdjacentProvinceA->IsCountyPalatine() && itInfo.pAdjacentProvinceA->IsSelect())
			{
				bFlicker = true;
			}
			else if (itInfo.pAdjacentProvinceB && itInfo.pAdjacentProvinceB->IsCountyPalatine() && itInfo.pAdjacentProvinceB->IsSelect())
			{
				bFlicker = true;
			}
			if (bFlicker)
			{
				V(m_pEffect->SetFloat("g_fTime", fTime));
			}
			else
			{
				V(m_pEffect->SetFloat("g_fTime", 0.f));
			}
			V(pd3dDevice->SetStreamSource(0, itInfo.pVB, 0, sizeof(_stStrategyBorderD3DVertex)));
			V(pd3dDevice->SetIndices(itInfo.pIB));
			UINT iPass, cPasses;
			V(m_pEffect->Begin(&cPasses, 0));
			for (iPass = 0; iPass < cPasses; ++iPass)
			{
				V(m_pEffect->BeginPass(iPass));
				V(pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, itInfo.dwVerticesNum, 0, itInfo.dwIndicesNum - 2));
				V(m_pEffect->EndPass());
			}
			V(m_pEffect->End());
		}
	}
}

void CStrategyProvinceBorder::OnLostDevice()
{
	if (m_pEffect)
	{
		m_pEffect->OnLostDevice();
	}
	for (auto& it : m_listBorderVerticInfo)
	{
		SAFE_RELEASE(it.pVB);
		SAFE_RELEASE(it.pIB);
	}
}

void CStrategyProvinceBorder::OnDestroyDevice()
{
	for (auto& it : m_listBorderVerticInfo)
	{
		SAFE_RELEASE(it.pVB);
		SAFE_RELEASE(it.pIB);
	}
	SAFE_RELEASE(m_pEffect);
	SAFE_RELEASE(m_pDecl);
}

HRESULT CStrategyProvinceBorder::CreateMeshWhenResetDevice(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	
	// 跟据省份信息 分不同map 用不同的border纹理 zwb_promising
	
	if (m_multimapAllBorder.empty())
	{
		return -1;
	}
	
	V_RETURN(BuildMesh(pd3dDevice));
	printf("Create Strategy Border Mesh Success!\n");
	
	return hr;
}

bool CStrategyProvinceBorder::TraverseProvinceBorderSegment(_stRGB* pProvinceRGB, _mapAllBorderSegment& mapBorderPoint) const
{
	// 用颜色是因为有些地点并不是省份
	CHECK_RE_FALSE(pProvinceRGB);

	for (int iH = 0; iH < c_nTerrainHeight; ++iH)
	{
		for (int jW = 0; jW < c_nTerrainWidth; ++jW)
		{
			DWORD index = CMathFunc::XYTran2Index(jW, iH, c_nTerrainWidth, c_nTerrainHeight);
			_stRGB rgbMine = pProvinceRGB[index];
			DWORD indexRGB = rgbMine.Trans2DWORD();
			// 遍历上左下右
			for (int eDir = DirMin; eDir < DirMax; ++eDir)
			{
				CPoint ptComp;
				if (CStrategyRenderFunc::DirTrans2XY((_eDirULDR)eDir, CPoint(jW, iH), ptComp))
				{
					// ptTemp 合法
					DWORD indexComp = CMathFunc::XYTran2Index(ptComp.x, ptComp.y, c_nTerrainWidth, c_nTerrainHeight);
					_stRGB rgbComp = pProvinceRGB[indexComp];
					if (rgbMine == rgbComp)
					{
						continue;
					}
					long long indexProvRGBPlus = 0;
					DWORD indexRGBCompare = rgbComp.Trans2DWORD();
					if (indexRGB > indexRGBCompare)
					{
						indexProvRGBPlus = CMathFunc::XYTran2Index(indexRGB, indexRGBCompare, RGBMAX, RGBMAX);
					}
					else
					{
						indexProvRGBPlus = CMathFunc::XYTran2Index(indexRGBCompare, indexRGB, RGBMAX, RGBMAX);
					}
					DWORD PointIndexBase = CMathFunc::XYTran2Index(jW > ptComp.x ? jW : ptComp.x, iH > ptComp.y ? iH : ptComp.y, c_nPlusHalfMaxWidth, c_nPlusHalfMaxHeight);	// 基础点
					DWORD PointIndexNext;
					switch((_eDirULDR)eDir)
					{
					case Up:
					case Down:
						PointIndexNext = PointIndexBase + 1;	// 右
						break;
					case Left:
					case Right:
						PointIndexNext = PointIndexBase + c_nPlusHalfMaxWidth;	// 上
						break;
					default:
						return false;
						break;
					}

					_stAdjacentProvincePtrNsetSegment stTemp;
					auto iterInsert = mapBorderPoint.insert(std::make_pair(indexProvRGBPlus, stTemp));
					if (iterInsert.second)
					{
						// 插入成功 第一次赋值 就给指针赋值
						const _stProvinceInfo* const* pEPP = CDataMgr::Singleton().GetProgramDataMgrCPtr()->GetProvinceDataCPtr()->GetEachPointProvincePointer();
						iterInsert.first->second.pAdjacentProvince1 = pEPP[index];
						iterInsert.first->second.pAdjacentProvince2 = pEPP[indexComp];
					}
					// 无论成功失败 向set里insert数据
					_stSegment stSegment(PointIndexBase, PointIndexNext);
					iterInsert.first->second.setSegment.insert(stSegment);
				}
			}
		}
	}
	return true;
}

bool CStrategyProvinceBorder::TransBorderSegment2Line(_mapAllBorderSegment& mapBorderSegment)
{
	for (auto& itmap : mapBorderSegment)
	{
		if (itmap.second.pAdjacentProvince1 && itmap.second.pAdjacentProvince2)
		{
			_stProvinceInfo* pProvince1 = const_cast<_stProvinceInfo*>(itmap.second.pAdjacentProvince1);
			_stProvinceInfo* pProvince2 = const_cast<_stProvinceInfo*>(itmap.second.pAdjacentProvince2);
			CDataMgr::Singleton().GetProgramDataMgrCPtr()->GetProvinceDataPtr()->InseradjacentProvince(pProvince1, pProvince2);
		}
		
		while (!itmap.second.setSegment.empty())
		{
			std::deque<_stBorderInfo> dequeBorderBody;
			// 随便选择遍历起点 这里选itmap.second.begin()->GetPosLess()
			int nBeginPos = itmap.second.setSegment.begin()->GetPosLess();
			int nNextPos = itmap.second.setSegment.begin()->GetPosLarge();
			itmap.second.setSegment.erase(itmap.second.setSegment.begin());
			TraverseSetSegment2FindNextPos(nNextPos, itmap.second.setSegment, false, dequeBorderBody);
			TraverseSetSegment2FindNextPos(nBeginPos, itmap.second.setSegment, true, dequeBorderBody);
			
			_stAdjacentProvincePtrNdequeBorder st(itmap.second.pAdjacentProvince1, itmap.second.pAdjacentProvince2);
			auto iterInsert = m_multimapAllBorder.insert(std::make_pair(itmap.first, st));
			iterInsert->second.dequeBorderBody = dequeBorderBody;
		}
	}
	return true;
}

void CStrategyProvinceBorder::TraverseSetSegment2FindNextPos(DWORD indexBegin, std::set<_stSegment>& setSegment, bool bPushFront, std::deque<_stBorderInfo>& dequeBorderBody) const
{
OptimizeTailRecursion:
	int nX, nY;
	CMathFunc::IndexTran2XY(&nX, &nY, indexBegin, c_nPlusHalfMaxWidth, c_nPlusHalfMaxHeight);
	_stBorderInfo st;
	st.vec2 = D3DXVECTOR2(nX, nY);
	if (bPushFront)
	{
		dequeBorderBody.push_front(st);
	}
	else
	{
		dequeBorderBody.push_back(st);
	}
	int indexNext = -1;
	for (auto iter = setSegment.begin(); iter != setSegment.end(); )
	{		
		if (iter->GetPosLess() == indexBegin)
		{
			indexNext = iter->GetPosLarge();
		}
		else if (iter->GetPosLarge() == indexBegin)
		{
			indexNext = iter->GetPosLess();
		}
		if (-1 == indexNext)
		{
			++iter;
		}
		else
		{
			iter = setSegment.erase(iter);
			break;
		}
	}
	if (-1 == indexNext)
	{
		return;
	}
	// 手动尾递归
	indexBegin = indexNext;
	goto OptimizeTailRecursion;
	//return TraverseSetSegment2FindNextPos(indexNext, setSegment, bPushFront, dequeBorderBody);
}

HRESULT CStrategyProvinceBorder::BuildMesh(IDirect3DDevice9* pd3dDevice)
{
	HRESULT hr = S_OK;
	m_listBorderVerticInfo.clear();
	for (const auto& it : m_multimapAllBorder)
	{
		int nSize = (int)it.second.dequeBorderBody.size();
		if (nSize < 2)
		{
			CString strErr(L"Border Line is too short!");
			CommonFunc::ErrorMessageBox(strErr);
			return -1;
		}
		_stStrategyBorderVerticesInfo st;
		st.dwVerticesNum = 2 * nSize;
		{
			DWORD indexVB = 0;
			_stStrategyBorderD3DVertex *pVertices;
			V_RETURN(pd3dDevice->CreateVertexBuffer(st.dwVerticesNum * sizeof(_stStrategyBorderD3DVertex),
				0, 0, D3DPOOL_DEFAULT, &st.pVB, NULL));
			V_RETURN(st.pVB->Lock(0, 0, (void**)&pVertices, 0));
			
			for (int i = 0; i < nSize; ++i)
			{
				D3DXVECTOR2 nVec90, nVec270, vOff;
				if (0 == i)
				{
					vOff = it.second.dequeBorderBody[1].vec2 - it.second.dequeBorderBody[0].vec2;
				}
				else if (nSize - 1 == i)
				{
					vOff = it.second.dequeBorderBody[nSize - 1].vec2 - it.second.dequeBorderBody[nSize - 2].vec2;
				}
				else
				{
					vOff = it.second.dequeBorderBody[i + 1].vec2 - it.second.dequeBorderBody[i - 1].vec2;
				}
				D3DXVec2Normalize(&nVec90, &CMathFunc::Vec2Rotate(vOff, D3DX_PI / 2.f));
				nVec270 = -nVec90;

				float fWidth = GetBorderWidth();

				nVec90 *= fWidth / 2.f;
				nVec90 += it.second.dequeBorderBody[i].vec2;

				pVertices[indexVB].x = nVec90.x;
				pVertices[indexVB].y = GetBorderHeight(nVec90.x, nVec90.y);
				pVertices[indexVB].z = nVec90.y;
				pVertices[indexVB].u = i;
				pVertices[indexVB].v = 1.f;
				st.setAreaIn.insert(CStrategyRenderFunc::GetRenderSquareIndex(nVec90.x, nVec90.y));
				++indexVB;

				nVec270 *= fWidth / 2.f;
				nVec270 += it.second.dequeBorderBody[i].vec2;

				pVertices[indexVB].x = nVec270.x;
				pVertices[indexVB].y = GetBorderHeight(nVec270.x, nVec270.y);
				pVertices[indexVB].z = nVec270.y;
				pVertices[indexVB].u = i;
				pVertices[indexVB].v = 0.f;
				st.setAreaIn.insert(CStrategyRenderFunc::GetRenderSquareIndex(nVec270.x, nVec270.y));
				++indexVB;

				st.pAdjacentProvinceA = it.second.pAdjacentProvince1;				
				st.pAdjacentProvinceB = it.second.pAdjacentProvince2;
			}
			V_RETURN(st.pVB->Unlock());
		}

		st.dwIndicesNum = st.dwVerticesNum;		// 三角形带 保证顺时针
		{
			DWORD* pIndices;
			V_RETURN(pd3dDevice->CreateIndexBuffer(st.dwIndicesNum * sizeof(DWORD),
				0, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &st.pIB, NULL));
			V_RETURN(st.pIB->Lock(0, 0, (void**)&pIndices, 0));
			for (DWORD i = 0; i < st.dwIndicesNum; ++i)
			{
				pIndices[i] = i;
			}
			V_RETURN(st.pIB->Unlock());
		}

		m_listBorderVerticInfo.push_back(st);
		if (st.pAdjacentProvinceA)
		{
			auto pProvinceInfo = const_cast<_stProvinceInfo*>(st.pAdjacentProvinceA);
			if (pProvinceInfo)
			{
				pProvinceInfo->setProvinceBorder.insert(&m_listBorderVerticInfo.back());
			}
		}
		if (st.pAdjacentProvinceB)
		{
			auto pProvinceInfo = const_cast<_stProvinceInfo*>(st.pAdjacentProvinceB);
			if (pProvinceInfo)
			{
				pProvinceInfo->setProvinceBorder.insert(&m_listBorderVerticInfo.back());
			}
		}
	}
	CProvinceData* pProvinceData = CDataMgr::Singleton().GetProgramDataMgrCPtr()->GetProvinceDataPtr();
	if (pProvinceData)
	{
		pProvinceData->InitAreaIn();		// Mesh的setAreaIn数据已经完全了
	}
	return hr;
}

inline float CStrategyProvinceBorder::GetBorderWidth() const
{
	return c_fBorderWidthN;
}

float CStrategyProvinceBorder::GetBorderHeight(float x, float y) const
{
	return CStrategyRenderFunc::GetHeightByFloatXY(x, y) * c_fHeightMapScale;
}

bool CStrategyProvinceBorder::CreateProvinceBorderInfo(_stRGB* pProvinceRGB)
{
	// 先遍历出所有边界点
	_mapAllBorderSegment mapBorderPoint;	// 存入的点 经过+0.5处理成为整数
	if (!TraverseProvinceBorderSegment(pProvinceRGB, mapBorderPoint))		// 点的xy各加了0.5 -0.5~3071.5 => 0~3072
	{
		return false;
	}
	printf("Traverse Strategy Border Point Success!\n");

	// 再根据所有边界线段组成边界线,存入multimap
	if (!TransBorderSegment2Line(mapBorderPoint))
	{
		return false;
	}
	printf("Transform Strategy Border Point to Line Success!\n");

	for (auto& it : m_multimapAllBorder)
	{
		if (!CStrategyRenderFunc::RebuildBodyList(it.second.dequeBorderBody, c_nBorderFuzzy, false, NULL, -0.5f, true))	// 还原标准xy
		{
			return false;
		}
	}
	return true;
}